﻿Imports System.Text.RegularExpressions

Public Class frmMain
    Dim WithEvents input As SRTFile
    Dim WithEvents output As SUBFile

    Private Sub btnApri_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnApri.Click
        With OpenFile
            If .ShowDialog(Me) = Windows.Forms.DialogResult.OK Then
                txtInput.Text = .FileName
                lblSize.Text = GetSizeString(My.Computer.FileSystem.GetFileInfo(.FileName).Length)
                input = New SRTFile(Single.Parse(lblFPS.Tag), .FileName)
                input.LoadFromFile()
                lblCount.Text = "Sottotitolo caricato. " & input.Campi.Count & " entità."
                grpOut.Enabled = True
            End If
        End With
    End Sub

    Public Sub SetCurrentSub(ByVal entry As SubtitleEntry)
        lblSub.Text = String.Format("Sottotitolo numero {0}.   Inizio: {1}    Fine: {2}" & vbCrLf & entry.text.Replace(vbCr, vbCrLf), entry.num, entry.startTime.ToString, entry.endTime.ToString)
    End Sub

    Public Shared Function GetSizeString(ByVal length As Long) As String
        If length < 1024 Then
            Return length & " bytes"
        ElseIf length < 1024 * 1024 Then
            Return CInt(length / 1024) & " Kb"
        Else
            Return CInt(length / 1024 / 1024) & " Mb"
        End If
    End Function

    Private Sub UpdateProgress(ByVal sender As Object, ByVal e As progressInfo) Handles input.Progress, output.Progress
        If e.TotaleCampi <> prgStatus.Maximum Then prgStatus.Maximum = e.TotaleCampi
        prgStatus.Value = e.CampoCorrente
        lblStatus.Text = e.Stato
    End Sub

    Private Sub btnRefreshIn_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnRefreshIn.Click
        input = New SRTFile(Single.Parse(lblFPS.Tag), txtInput.Text)
        input.LoadFromFile()
    End Sub

    Private Sub btnSalva_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnSalva.Click
        With SaveFile
            If .ShowDialog(Me) = Windows.Forms.DialogResult.OK Then
                txtOutput.Text = .FileName
                output = New SUBFile(Single.Parse(lblFPS.Tag), .FileName, input.Campi)
                lblStatus.Text = "File di output selezionato."
                btnStart.Enabled = True
            End If
        End With
    End Sub

    Private Sub btnRefreshOut_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnRefreshOut.Click
        output = New SUBFile(Single.Parse(lblFPS.Tag), txtOutput.Text, input.Campi)
    End Sub

    Private Sub lblFPS_LinkClicked(ByVal sender As System.Object, ByVal e As System.Windows.Forms.LinkLabelLinkClickedEventArgs) Handles lblFPS.LinkClicked
        If frmFPS.ShowDialog(Me, Single.Parse(lblFPS.Tag)) = DialogResult.OK Then
            lblFPS.Tag = frmFPS.FPS
            lblFPS.Tag = frmFPS.FPS & " FPS"
        End If
    End Sub

    Private Sub btnStart_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStart.Click
        output.SaveToFile()
    End Sub
End Class

Public Class SRTFile
    Public Campi As New List(Of SubtitleEntry)
    Public fps As Single
    Public path As String

    Public Sub New(ByVal framespersecond As Single, ByVal file As String)
        fps = framespersecond
        path = file
    End Sub

    Public Sub LoadFromFile()
        Dim fileContents As String
        fileContents = My.Computer.FileSystem.ReadAllText(path).Trim()

        ' Get total count
        Dim subs As List(Of String()) = SeparateEntries(fileContents)
        Dim i As Integer = 0
        Dim tmp As SubtitleEntry
        For Each subo As String() In subs
            RaiseEvent Progress(Me, New progressInfo(i, subs.Count, "Parsing entità sottotitolo " & i & " di " & subs.Count))
            tmp = ParseEntry(subo)
            Campi.Add(tmp)
            frmMain.SetCurrentSub(tmp)
            i += 1
            If i Mod 10 = 0 Then Application.DoEvents()
        Next

        RaiseEvent Progress(Me, New progressInfo(subs.Count, subs.Count, "Parsing sottotitolo completato: " & subs.Count & " entità."))

    End Sub

    Private Function SeparateEntries(ByVal Subtitles As String) As List(Of String())
        Dim righe As String() = Subtitles.Split(vbCrLf)
        Dim inizioEntry As Integer = 0
        SeparateEntries = New List(Of String())
        righe = TrimRows(righe)
        ReDim Preserve righe(righe.Count)
        righe(righe.Count - 1) = ""     ' Hack per non perdere l'ultimo sottotitolo

        For i As Integer = 0 To righe.Count - 1
            RaiseEvent Progress(Me, New progressInfo(i, righe.Count, "Individuazione entità sottotitoli: riga " & i & " di " & righe.Count))
            If righe(i) = "" Then
                ' Inizia una nuova entry!
                Dim tmp(i - 1 - inizioEntry) As String
                For k As Integer = 0 To i - 1 - inizioEntry
                    tmp(k) = righe(inizioEntry + k)
                Next
                SeparateEntries.Add(tmp)
                inizioEntry = i + 1
            End If
            If i Mod 10 = 0 Then Application.DoEvents()
        Next
        'Return SeparateEntries
    End Function

    Private Function TrimRows(ByRef rows As String()) As String()
        For i As Integer = 0 To rows.Count - 1
            rows(i) = rows(i).Trim(Chr(10) & Chr(13) & " ")
        Next
        Return rows
    End Function

    Private Function ParseEntry(ByVal righe() As String) As SubtitleEntry
        ' SRT Entry format:
        ' {numero sottotitolo}
        ' {hh:mm:ss,mls} --> {hh:mm:ss,mls}
        ' {Testo}
        ' hh:mm:ss,mls --> hh:mm:ss,mls

        Dim num As Integer = Integer.Parse(righe(0))
        If num < 1 Then Throw New System.ArgumentException("Il numero del sottotitolo è minore di uno." & vbCrLf & "Riga: " & righe(0))
        Dim testo As String = ""
        For i As Integer = 2 To righe.Count - 1
            ' Ottengo il testo
            testo += righe(i) & Chr(13)
        Next
        Dim inizio, fine As SubtitleTime
        inizio = New SubtitleTime(Integer.Parse(righe(1).Substring(0, 2)), _
                                  Integer.Parse(righe(1).Substring(3, 2)), _
                                  Integer.Parse(righe(1).Substring(6, 2)), _
                                  Integer.Parse(righe(1).Substring(9, 3)), fps)
        fine = New SubtitleTime(Integer.Parse(righe(1).Substring(17, 2)), _
                                  Integer.Parse(righe(1).Substring(20, 2)), _
                                  Integer.Parse(righe(1).Substring(23, 2)), _
                                  Integer.Parse(righe(1).Substring(26, 3)), fps)

        Return New SubtitleEntry(num, testo, inizio, fine)
    End Function

    Public Event Progress(ByVal sender As System.Object, ByVal e As progressInfo)
End Class

Public Class progressInfo
    Public CampoCorrente As Integer
    Public TotaleCampi As Integer
    Public Stato As String

    Public Sub New(ByVal current As Integer, ByVal total As String, ByVal status As String)
        CampoCorrente = current
        TotaleCampi = total
        Stato = status
    End Sub
End Class

Public Class SUBFile
    Public Campi As New List(Of SubtitleEntry)
    Public fps As Single
    Public path As String

    Public Sub New(ByVal framespersecond As Single, ByVal file As String, ByVal entries As List(Of SubtitleEntry))
        fps = framespersecond
        path = file
        Campi = entries
    End Sub

    Public Event Progress(ByVal sender As System.Object, ByVal e As progressInfo)

    Public Sub SaveToFile()
        Dim res As String = ""
        Dim i As Integer = 0
        For Each entry As SubtitleEntry In Campi
            frmMain.SetCurrentSub(entry)
            res += CreateEntry(entry)
            i += 1
            RaiseEvent Progress(Me, New progressInfo(i, Campi.Count, "Conversione entità sottotitolo " & i & " di " & Campi.Count))
            If i Mod 10 = 0 Then Application.DoEvents()
        Next

        RaiseEvent Progress(Me, New progressInfo(0, 1, "Scrittura file SUB in corso..."))
        My.Computer.FileSystem.WriteAllText(path, res, False, System.Text.Encoding.Unicode)
        RaiseEvent Progress(Me, New progressInfo(1, 1, "Scrittura file SUB completata!"))
    End Sub

    Private Function CreateEntry(ByVal entry As SubtitleEntry) As String
        ' Formato DVD Architect Subtitles Script:
        ' {num, 4}{"  "}{hh:mm:ss:ff}{"  "}{hh:mm:ss:ff}{"  "}{Text Line 1}{vbCrLf} 
        ' {Text Line 2}{vbCrLf}
        ' {vbCrLf}
        Dim tag As Match = Regex.Match(entry.text, "<[a-zA-Z]>", RegexOptions.IgnoreCase)
        While tag.Success = True
            entry.text = entry.text.Remove(tag.Index, tag.Length)
            tag = Regex.Match(entry.text, "<[a-zA-Z]>", RegexOptions.IgnoreCase)
        End While

        tag = Regex.Match(entry.text, "</[a-zA-Z]>", RegexOptions.IgnoreCase)
        While tag.Success = True
            entry.text = entry.text.Remove(tag.Index, tag.Length)
            tag = Regex.Match(entry.text, "</[a-zA-Z]>", RegexOptions.IgnoreCase)
        End While

        CreateEntry = CStr(entry.num).PadLeft(4, "0") & "  " & _
                      entry.startTime.ToStringInFrames() & "  " & _
                      entry.endTime.ToStringInFrames & "  " & _
                      entry.text & vbCrLf & vbCrLf
    End Function
End Class

Public Class SubtitleEntry
    Public num As Integer
    Public text As String
    Public startTime As SubtitleTime
    Public endTime As SubtitleTime

    Public Sub New(ByVal numero As Integer, ByVal testo As String, ByVal inizio As SubtitleTime, ByVal fine As SubtitleTime)
        num = numero
        text = testo
        startTime = inizio
        endTime = fine
    End Sub
End Class

Public Class SubtitleTime
    Public h, m, s, ms As Integer
    Public fps As Single

    Public Sub New(ByVal ore As Integer, ByVal minuti As Integer, ByVal secondi As Integer, ByVal millisecondi As Integer, ByVal framespersecond As Single)
        h = ore
        m = minuti
        s = secondi
        ms = millisecondi
        fps = framespersecond
    End Sub

    Public Overrides Function ToString() As String
        Return CStr(h).PadLeft(2, "0") & ":" & CStr(m).PadLeft(2, "0") & ":" & CStr(s).PadLeft(2, "0") & "," & CStr(ms).PadLeft(3, "0")
    End Function

    Private Function GetFramesFromMs() As Integer
        Return CInt(fps * (ms / 1000))
    End Function

    Public Function ToStringInFrames() As String
        Return CStr(h).PadLeft(2, "0") & ":" & CStr(m).PadLeft(2, "0") & ":" & CStr(s).PadLeft(2, "0") & ":" & CStr(GetFramesFromMs()).PadLeft(2, "0")
    End Function
End Class

